package routes

import (
	"encoding/json"
	"log"
	"net/http"
	"reflect"
	"strconv"

	"fabrictask/sdkapi"
	"fabrictask/utils"
	"fmt"

	"github.com/gin-gonic/gin"
	"github.com/go-session/gin-session"
)

type RespMsg struct {
	Code string      `json:"code"`
	Msg  string      `json:"msg"`
	Data interface{} `json:"data"`
}

type TaskUser struct {
	UserID   string `json:"username"`
	UserName string `json:"nickname"`
	PassWord string `json:"password"`
}

//resp数据响应
func ResponseData(c *gin.Context, resp *RespMsg) {
	resp.Msg = utils.RecodeText(resp.Code)
	c.JSON(http.StatusOK, resp)
}

func Login(c *gin.Context) {
	resp := &RespMsg{
		"0",
		"OK",
		nil,
	}
	defer ResponseData(c, resp)
	loginuser := TaskUser{}
	c.Bind(&loginuser)

	fmt.Println("login:", loginuser)
	jsonData, err := sdkapi.Login(loginuser.UserID, loginuser.PassWord)
	if err != nil {
		resp.Code = utils.RECODE_CALL_CC_ERR
		return
	}
	//var ccResp RespMsg
	err = json.Unmarshal(jsonData, &resp)
	if err != nil {
		fmt.Printf("failed to Unmarshal cc result:%+v\n", err)
		resp.Code = utils.RECODE_JSON_ERR
		return
	}

	fmt.Printf("login result:%+v\n", resp)
	store := ginsession.FromContext(c)
	store.Set("userid", loginuser.UserID)
	store.Set("nickname", resp.Data.(string))
	err = store.Save()
	if err != nil {
		resp.Code = utils.RECODE_SESSION_ERR
		return
	}

	return
}

func LogOut(c *gin.Context) {
	resp := &RespMsg{
		"0",
		"OK",
		nil,
	}
	defer ResponseData(c, resp)

	err := ginsession.Destroy(c)
	if err != nil {
		log.Printf("failed to logout destory session", err)
		return
	}

	return
}

func Register(c *gin.Context) {
	resp := &RespMsg{
		"0",
		"OK",
		nil,
	}
	defer ResponseData(c, resp)

	tuser := TaskUser{}

	err := c.Bind(&tuser)
	if err != nil {
		fmt.Println("Failed to bind params")
		resp.Code = utils.RECODE_PARAMERR
		return
	}
	fmt.Printf("register request:%+v\n", tuser)

	respJson, err := sdkapi.Register(tuser.UserID, tuser.UserName, tuser.PassWord)

	if err != nil {
		resp.Code = utils.RECODE_CALL_CC_ERR
		return
	}

	err = json.Unmarshal(respJson, &resp)
	if err != nil {
		resp.Code = utils.RECODE_JSON_ERR
		return
	}
	if resp.Code != utils.RECODE_OK {
		log.Printf("Register err: %s\n", resp.Msg)
		return
	}
	fmt.Printf("register:%+v\n", resp)
	// mint to user
	txID, err := sdkapi.Mint(tuser.UserID, 100)
	if err != nil {
		resp.Code = utils.RECODE_CALL_CC_ERR
		return
	}
	// err = json.Unmarshal(respJson, &resp)
	// if err != nil {
	// 	resp.Code = utils.RECODE_JSON_ERR
	// 	return
	// }
	// if resp.Code != utils.RECODE_OK {
	// 	log.Printf("Register Mint err: %s\n", resp.Msg)
	// }
	fmt.Printf("register mint:%+v, txid(%v)\n", resp, string(txID))

	return
}

type ReqMint struct {
	UserID string `userid`
	Amount int    `amount`
}

func Mint(c *gin.Context) {
	resp := &RespMsg{
		"0",
		"OK",
		nil,
	}
	defer ResponseData(c, resp)

	reqMint := ReqMint{}

	err := c.Bind(&reqMint)
	if err != nil {
		fmt.Println("Failed to bind params")
		resp.Code = utils.RECODE_PARAMERR
		return
	}
	fmt.Printf("Mint request:%+v\n", reqMint)

	txID, err := sdkapi.Mint(reqMint.UserID, reqMint.Amount)
	if err != nil {
		resp.Code = utils.RECODE_CALL_CC_ERR
		return
	}
	fmt.Printf("register mint:%+v, txid(%v)\n", resp, string(txID))

	return
}

// 前端请求类型
type TaskIssueReq struct {
	TaskID  string `json:"task_id"`
	Issuer  string `json:"issuer"`
	Hunter  string `json:"task_user"`
	Comment string `json:"comment"`
	Status  int8   `json:"task_status"`
	Desc    string `json:"task_name"`
	Bonus   int    `json:"bonus"`
}

func Issue(c *gin.Context) {
	resp := &RespMsg{
		"0",
		"OK",
		nil,
	}
	defer ResponseData(c, resp)

	// 解析前端请求数据
	task := TaskIssueReq{}
	err := c.Bind(&task)
	if err != nil {
		fmt.Println("Failed to bind params", err)
		resp.Code = utils.RECODE_PARAMERR
		return
	}
	fmt.Printf("Issue:%+v\n", task)
	store := ginsession.FromContext(c)
	userid, ok := store.Get("userid")
	if !ok {
		resp.Code = utils.RECODE_SESSION_ERR
		return
	}

	respJson, err := sdkapi.IssueTask(task.Desc, userid.(string), task.Bonus)

	//respJson, err := sdkapi.IssueTask(task.Desc, task.Issuer, task.Bonus)

	if err != nil {
		resp.Code = utils.RECODE_CALL_CC_ERR
		return
	}

	err = json.Unmarshal(respJson, &resp)
	if err != nil {
		resp.Code = utils.RECODE_JSON_ERR
		return
	}

	return
}

func Update(c *gin.Context) {
	resp := &RespMsg{
		"0",
		"OK",
		nil,
	}
	defer ResponseData(c, resp)

	task := TaskIssueReq{}
	err := c.Bind(&task)
	if err != nil {
		fmt.Println("Failed to bind params")
		resp.Code = utils.RECODE_PARAMERR
		return
	}
	fmt.Println(task)
	fmt.Printf("Issue:%+v\n", task)
	store := ginsession.FromContext(c)
	userid, ok := store.Get("userid")
	if !ok {
		resp.Code = utils.RECODE_SESSION_ERR
		return
	}

	respJson, err := sdkapi.ChangeTask(task.TaskID, userid.(string), task.Comment, task.Status)
	if err != nil {
		fmt.Println("Modify err:", err)
		resp.Code = utils.RECODE_CALL_CC_ERR
		return
	}

	err = json.Unmarshal(respJson, &resp)
	if err != nil {
		resp.Code = utils.RECODE_JSON_ERR
		return
	}

	return
}

func parseTaskJson(respJson []byte) ([]TaskIssueReq, error) {
	var tasks []TaskIssueReq
	var resp RespMsg
	err := json.Unmarshal(respJson, &resp)
	if err != nil {
		fmt.Printf("failed to parseTaskJson-Unmarshal %+v\n", err)
		return tasks, err
	}
	if resp.Data == nil {
		return tasks, nil
	}
	//fmt.Printf("parseTask:%+v\n", resp)

	fmt.Printf("org:%v\n", reflect.TypeOf(resp.Data))
	orgtasks := resp.Data.([]interface{})
	//fmt.Println("len= ", len(orgtasks))
	fmt.Println("type is ", reflect.TypeOf(orgtasks[0]))

	//var cctasks []sdkapi.FabTask
	// err = json.Unmarshal([]byte(taskData), &cctasks)
	// if err != nil {
	// 	fmt.Println("Failed to unmarshal:", err)
	// 	return tasks, err
	// }

	var task TaskIssueReq
	for _, vv := range orgtasks {
		v := vv.(map[string]interface{})
		task.TaskID = fmt.Sprintf("%v", v["task_id"])
		//task.Bonus = v["bonus"].(int)
		val := fmt.Sprintf("%v", v["bonus"])
		task.Bonus, _ = strconv.Atoi(val)
		task.Issuer = v["issuer"].(string)
		task.Hunter = v["holder"].(string)
		task.Comment = v["comment"].(string)
		task.Desc = v["desc"].(string)
		val = fmt.Sprintf("%v", v["status"])
		val1, _ := strconv.Atoi(val)
		task.Status = int8(val1)
		tasks = append(tasks, task)
	}

	return tasks, nil
}

func TaskList(c *gin.Context) {
	resp := &RespMsg{
		"0",
		"OK",
		nil,
	}
	defer ResponseData(c, resp)
	page := c.Query("page")
	if page == "" {
		page = "1"
	}
	respJson, err := sdkapi.AllTasks(page)
	if err != nil {
		fmt.Println("failed to get all tasks：", err)
		resp.Code = utils.RECODE_CALL_CC_ERR
		return
	}

	tasks, err := parseTaskJson(respJson)
	if err != nil {
		resp.Code = utils.RECODE_JSON_ERR
		return
	}

	//begin, end := 0, 10
	ipage, _ := strconv.Atoi(page)
	ipage = ipage / 10

	begin := ipage * 10
	if begin > len(tasks) {
		begin = 0
	}
	end := begin + 10
	if end > len(tasks) {
		end = len(tasks)
	}

	fmt.Println("begin = ", begin, ", end = ", end, ",page = ", page)
	fmt.Println(tasks)
	ts := struct {
		Total int         `json:"total"`
		Data  interface{} `json:"data"`
	}{
		Total: len(tasks),
		Data:  tasks[begin:end],
	}
	resp.Data = ts
	return
}

type BalanceResp struct {
	Account string `json:"account"`
	Amount  uint64 `json:"amount"`
}

// /balance?username=yekai
func BalanceOf(c *gin.Context) {
	//1. 设置响应消息
	resp := RespMsg{
		Code: "0",
	}
	//2. 延迟执行响应消息
	defer ResponseData(c, &resp)

	username, ok := c.GetQuery("username")
	if !ok {
		fmt.Println("username does not exists")
		resp.Code = utils.RECODE_PARAMERR
		return
	}

	//3. eos 获取余额

	value, err := sdkapi.BalanceOf(username)
	if err != nil {
		fmt.Println("failed to eos.QueryTask ", err)
		resp.Code = utils.RECODE_CALL_CC_ERR
		return
	}
	amount, _ := strconv.Atoi(string(value))
	balanceResp := BalanceResp{username, uint64(amount)}

	resp.Data = balanceResp
}
